package com.tdg.qywx.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tdg.qywx.core.TDGResponse;
import com.tdg.qywx.core.enums.TDGResponseCode;
import com.tdg.qywx.mapper.MoMapper;
import com.tdg.qywx.mapper.MtrlMapper;
import com.tdg.qywx.mapper.PerMapper;
import com.tdg.qywx.model.*;
import com.tdg.qywx.utils.DoubleUtil;
import com.tdg.qywx.utils.publicUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class PerService {
    private static final Logger _logger = LoggerFactory.getLogger(PerService.class);

    @Autowired
    private PerMapper perMapper;
    @Autowired
    private MoMapper moMapper;
    @Autowired
    private MtrlMapper mtrlMapper;

    private final SimpleDateFormat DATE_SDF = new SimpleDateFormat("yyyy-MM-dd");
    private final SimpleDateFormat DATETIME_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 注意大写HH是24小时制

    /**
     * 计算时间差（小时）
     * @param start 开始时间
     * @param end 结束时间
     * @return 时间差(小时)
     */
    private double calcWorkHour(Date start, Date end) {
        double diffHours = (end.getTime() - start.getTime()) / 3600000.0;
        return new BigDecimal(diffHours).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 获取工单工序已生产数量
     * @param mo 工单号
     * @param sec 工艺段
     * @return 已生产数量
     */
    private double getYSCSL(String mo,String sec){
        Double yscsl = Double.valueOf("0.00");
        Map<String, Object> mapYSCSL = perMapper.findYSCSL(mo,sec);
        if(mapYSCSL != null && null!=mapYSCSL.get("YSCSL")){
            yscsl = Double.valueOf(mapYSCSL.get("YSCSL").toString());
        }
        _logger.info("当前工单:{},工艺段:{},实际已生成数量:{}",mo,sec,yscsl);
        return yscsl;
    }

    /**
     * 根据部门查找所有在线员工
     * @param bmid 部门ID
     * @return TDGResponse
     */
    public TDGResponse searchPersonAllByBM(String bmid){
        if (bmid == null || bmid.equals("")){
            bmid = "全部";
        }
        JSONArray array = new JSONArray();
        List<VW_TDG_UPH_EMP> perlist = perMapper.findOnlineEmp("","","",bmid);
        for (int i = 0; i < perlist.size(); i++) {
            JSONObject o = new JSONObject();
            VW_TDG_UPH_EMP person =  perlist.get(i);
            o.put("ID",person.getEMP_ID());
            o.put("NAME",person.getEMP_NAME());
            o.put("STATE",person.getSTATE());
            o.put("MO",person.getMO());
            o.put("SEC",person.getSEC());
            array.add(o);
        }
        return TDGResponse.success(array);
    }

    /**
     * 查找上线员工的部门
     * @return TDGResponse
     */
    public TDGResponse searchBM(){
        JSONArray array = new JSONArray();
        List<Map<String, Object>> buList = perMapper.findALLBM();
        for (Map<String, Object> map : buList) {
            JSONObject o = new JSONObject();
            String BU_ID = map.get("BU_ID").toString();
            o.put("BU_ID", BU_ID);
            array.add(o);
        }
        return TDGResponse.success(array);
    }

    /**
     * 根据工单号带出工单信息
     * @param gdh 工单号
     * @return TDGResponse: 三个月内的未关单的工单信息
     */
    public TDGResponse searchGD(String gdh){
        Calendar calendar = Calendar.getInstance(); //创建Calendar 的实例
        calendar.add(Calendar.DAY_OF_MONTH, -150); //当前时间减去3个月
        String tdate = DATETIME_SDF.format(calendar.getTime());
        tb_pp_mo mo = moMapper.findMoByMoAndTdate(gdh,tdate);
        if(mo!=null){
            JSONObject j = new JSONObject();
            j.put("PL_ID",mo.getPL_ID());
            j.put("PROD_ID",mo.getPROD_ID());
            j.put("P_QTY",mo.getP_QTY());
            j.put("MTRL_DESC",mo.getPROD_MODEL());
            return TDGResponse.success(j);
        }else{
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"未找到工单信息,或者该工单已完工,请确认工单号以及该工单状态");
        }
    }

    /**
     * 模糊查找工单号
     * @param gdh
     * @return TDGResponse 三个月内的未关单的工单信息
     */
    public TDGResponse searchGDLike(String gdh){
        if(gdh==""){
            gdh = "!@#$%^";
        }
        JSONArray array = new JSONArray();
        gdh = gdh+"%";

        Calendar calendar = Calendar.getInstance(); //创建Calendar 的实例
        calendar.add(Calendar.DAY_OF_MONTH, -150); //当前时间减去3个月
        String tdate = DATETIME_SDF.format(calendar.getTime());
        List<tb_pp_mo> moList = moMapper.findMoByMoLike(gdh, tdate);

        for (tb_pp_mo tbPpMo : moList) {
            JSONObject o = new JSONObject();
            o.put("MO", tbPpMo.getMO());
            array.add(o);
        }
        return TDGResponse.success(array);
    }

    /**
     * 查询所有工单号
     * @return TDGResponse 所有未关单的工单信息
     */
    public TDGResponse findMoByAll(){
        JSONArray array = new JSONArray();
        List<tb_pp_mo> moList = moMapper.findMoByAll();
        for (tb_pp_mo tbPpMo : moList) {
            JSONObject o = new JSONObject();
            o.put("MO", tbPpMo.getMO());
            array.add(o);
        }
        return TDGResponse.success(array);
    }

    /**
     * 根据工单号 查询工艺段
     * @param gdh 工单号
     * @return TDGResponse 当前工单的工艺段
     */
    public TDGResponse searchGYD(String gdh){
        JSONArray array = new JSONArray();
        List<Map<String, Object>> mapList =  mtrlMapper.selectGYD(gdh);
        for (Map<String, Object> map : mapList) {
            JSONObject o = new JSONObject();
            String PROD_SEC = map.get("PROD_SEC").toString();
            o.put("PROD_SEC", PROD_SEC);
            array.add(o);
        }
        return TDGResponse.success(array);
    }

    /**
     * 查询员工信息
     * @param id 员工ID
     * @param mo 工单号
     * @param sec 工艺段
     * @return TDGResponse
     */
    public TDGResponse searchPerson(String id,String mo,String sec) {
        tb_tdg_uph_emp emp = perMapper.findEmpInfoById(id);
        if(emp ==null){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"未找到该员工或该员工已离职");
        }

        JSONObject j = new JSONObject();
        j.put("name",emp.getEMP_NAME());

        List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(id, mo, sec,null);
        if (empList != null && empList.size() > 0) {
            j.put("state",empList.get(0).getSTATE());
        }
        JSONArray array = new JSONArray();
        for (VW_TDG_UPH_EMP iter: empList) {
            JSONObject o = new JSONObject();
            o.put("mo", iter.getMO());
            o.put("scxm",iter.getSCXM());
            o.put("sec",iter.getSEC());
            o.put("bb",iter.getSHIFT_TYPE());
            array.add(o);
        }
        j.put("molist", array);
        return TDGResponse.success(j);
    }

    /**
     * 查询当前工单和工艺段的上线员工
     * @param gdh 工单号
     * @param sec 工艺段
     * @return TDGResponse
     */
    public TDGResponse searchGDSetPer(String gdh, String sec){
        JSONArray array = new JSONArray();
        List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(null,gdh, sec,null);
        for (int i = 0; i < empList.size(); i++) {
            JSONObject o = new JSONObject();
            VW_TDG_UPH_EMP per =  empList.get(i);
            o.put("id",per.getEMP_ID());
            o.put("name",per.getEMP_NAME());
            o.put("mo",gdh);
            o.put("time", DATETIME_SDF.format(per.getB_TIME()));
            o.put("rptid",per.getRPT_ID());
            array.add(o);
        }
        return TDGResponse.success(array);
    }

    /**
     * 判断是否为一人多机
     * @param mo 工单号
     * @param sec 工艺段
     * @return Y:一人多机工艺段  N:非一人多机工艺段
     */
    private String getISMutli(String mo,String sec) {
        Map<String, Object> gydInfo = perMapper.findGYDInfo(mo, sec);
        String ISMutli = "N";
        if(gydInfo !=null){
            ISMutli = gydInfo.get("IS_MULTI").toString();
        }
        return ISMutli;
    }

    /**
     * 确认上线
     * @param functionID 模块号
     * @param opid 操作员
     * @param id 上线员工编号 EMP_ID
     * @param userName 上线员工姓名
     * @param bb 班别
     * @param mo 工单号
     * @param scxm 生产线码
     * @param datetime 上线时间
     * @param sec 工艺段
     * @return TDGResponse
     */
    @Transactional
    public TDGResponse confirmUp(String functionID,String opid, String id,String userName,String bb,String mo,String scxm,String datetime,String sec){
        _logger.debug("confirmUp ==> Transaction ID: {}", TransactionSynchronizationManager.getCurrentTransactionName());
        _logger.info("员工上线==>模块:{},操作员:{},工号:{},姓名：{},班别:{} ,工单:{},线码:{},工段:{},上线时间:{}",functionID,opid,id,userName,bb,mo,scxm,sec,datetime);

        tb_tdg_uph_emp empInfo = perMapper.findEmpInfoById(id);
        if(empInfo ==null){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"该员工不存在或已离职,不可上线");
        }

        tb_pp_mo moInfo =  moMapper.findMoById(mo);
        if (moInfo == null) {
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"该工单不存在,不可上线");
        }

        if ("Y".equalsIgnoreCase(moInfo.getIS_OVER())) {
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"该工单已关单,不可上线");
        }

        if (moInfo.getMO_STAT() == 1){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"该工单生产状态为队列，不可上线");
        }
        //当前工艺段是否为一人多机工序
        String ISMutli = getISMutli(mo,sec);

        List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(id,null,null,null);
        if ("N".equalsIgnoreCase(ISMutli)) {
            //非一人多机 则只可以上线一个工单
            if (empList != null && empList.size() > 0) {
                if (functionID.contains("QH")){
                    //切换工单时
                    return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"切换非一人多机工序失败：员工"+id+"已在工单"+empList.get(0).getMO()+"上线,不可重复上线");
                } else {
                    return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"非一人多机工序上线失败：员工"+id+"已在工单"+empList.get(0).getMO()+"上线,不可重复上线");
                }
            }
        } else if ("Y".equalsIgnoreCase(ISMutli)) {
            //一人多机工艺段  则可上线不同工单(但是同一工单只允许上线一个工艺段)
            for (int i=0;i<empList.size();i++) {
                VW_TDG_UPH_EMP person = empList.get(i);
                String isMutliFlag = getISMutli(person.getMO(), person.getSEC());
                if (!"Y".equalsIgnoreCase(isMutliFlag)) {
                    return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"只可上线同类型(一人多机)的工艺段");
                };
                if (mo.equalsIgnoreCase(person.getMO()) && !sec.equalsIgnoreCase(person.getSEC())) {
                    return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"当前工单其他工段["+person.getSEC()+"]已上线,不可上线");
                }
                if (mo.equalsIgnoreCase(person.getMO()) && sec.equalsIgnoreCase(person.getSEC())){
                    return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"当前工单当前工段已上线,不可重复上线");
                }
            }
        }

//       暂时不管控上线控制产量 后续放开
//        //判断工单必须有剩余产量 才可以进行上线
        Double yscsl = getYSCSL(mo,sec);
        String checkSJCLMsg = "";
        //剩余产量 = 工单计划数量 - 已生产数量
        if (new BigDecimal(DoubleUtil.sub(moInfo.getP_QTY(),yscsl)).compareTo(BigDecimal.ZERO) < 1){
//            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR, "此工单同工艺段无剩余产量,不可上线");
            checkSJCLMsg=",无剩余产量!";
        }

        //标准定额人数
        Integer rs = Integer.valueOf("0");
        List<Map<String, Object>> datamap = mtrlMapper.selectBZDE(publicUtil.getGdh1(mo),sec,scxm);
        if(datamap.size()>0){
            if(null!=datamap.get(0).get("UPH_DL")){
                rs = Integer.parseInt(datamap.get(0).get("UPH_DL").toString());
            }
        }
        List<VW_TDG_UPH_EMP> onlineEmpList = perMapper.findOnlineEmp(null, mo, sec, null);

        if (onlineEmpList.size() + 1 > rs){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR, "上线人数已超过定额设定人数,不可上线");
        }

        Boolean IS_NEED_INSERT_RPT = false;
        String rptid = null;
        List<tb_tdg_uph_rpt> itemList = perMapper.findRptByConditions(mo,scxm,"0",sec);



        // 如果存在此工单的报工数据 则直接返回此工单的报工表的RPT_ID
        if (itemList != null && itemList.size() > 0) {
            rptid = itemList.get(0).getRPT_ID();
        } else {
            rptid = UUID.randomUUID().toString();
            IS_NEED_INSERT_RPT  = true;
        }

        Date rptdate = new Date();
        Date UpDateTime = rptdate;
        String RPT_STATE = "";
        try {
            if("0".equals(bb)){
                //白班是8:00-18:00算正常报工，超过18:00叫加班报工
                String stime = DATE_SDF.format(rptdate)+" 08:00:00";
                String etime = DATE_SDF.format(rptdate)+" 18:00:00";
                if(publicUtil.isEffectiveDate(rptdate,DATETIME_SDF.parse(stime),DATETIME_SDF.parse(etime))){
                    RPT_STATE = "N";
                }else{
                    RPT_STATE = "A";
                }
            }else{
                //夜班是20:00-第二天早上6:00，超过时间算加班
                Calendar calendar = Calendar.getInstance(); //创建Calendar 的实例
                calendar.add(Calendar.DATE, 1); //后一天
                String stime = DATE_SDF.format(rptdate)+" 20:00:00";
                String etime = DATE_SDF.format(calendar.getTime()) +" 06:00:00";
                if(publicUtil.isEffectiveDate(rptdate,DATETIME_SDF.parse(stime),DATETIME_SDF.parse(etime))){
                    RPT_STATE = "N";
                }else{
                    RPT_STATE = "A";
                }
                String stime1 = DATE_SDF.format(rptdate)+" 00:00:00";
                String etime1 = DATE_SDF.format(rptdate) +" 12:00:00";
                if( rptdate.getTime()>=DATETIME_SDF.parse(stime1).getTime()&&
                        rptdate.getTime()<=DATETIME_SDF.parse(etime1).getTime()){
                    Calendar calendar1 = Calendar.getInstance(); //创建Calendar 的实例
                    calendar1.add(Calendar.DATE, -1); //后一天
                    rptdate = calendar1.getTime();
                }
            }

            rptdate = DATE_SDF.parse(DATE_SDF.format(rptdate));
        }catch (ParseException e) {
            _logger.error("员工上线==>日期转换异常:{}",e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return TDGResponse.fail(TDGResponseCode.ERROR,e.getCause().toString());
        }

        String remark = RPT_STATE;
        try {
            double pqty =  moInfo.getP_QTY();
            tb_tdg_uph_emp emp = perMapper.findEmpInfoById(id);
            String rptitemid = null;
            //1. 生成报工表
            if (IS_NEED_INSERT_RPT) {
                perMapper.insertRpt(functionID,rptid, moInfo.getPL_ID(),rptdate, bb,mo,emp.getBU_ID(), moInfo.getPROD_ID(), moInfo.getPROD_MODEL(),userName,moInfo.getCUST_ID(), moInfo.getCUST_NAME(),moInfo.getPL_NAME(),"0","X",remark,opid);
                rptitemid = UUID.randomUUID().toString();
                perMapper.insertRptItem(functionID,opid,rptid,sec,rptitemid,UpDateTime,pqty);
            } else {
                List<tb_tdg_uph_rpt_item> rptItemList =  perMapper.findRptItemByConditions(rptid,sec,"0",mo);
                rptitemid = rptItemList.get(0).getRPT_ITEM_ID();
            }

            //1. 更新考核关系表 TB_TDG_UPH_EMP
            perMapper.updateEmpUp(mo,scxm,sec,rptid,opid,id);
            //2. 插入上下线操作记录表 tb_bs_operate
            perMapper.insertOperate(functionID,opid,id,empInfo.getEMP_NAME(),0,mo,datetime,rptid,sec);
            //3. 更新员工报工表 TB_TDG_UPH_RPT_EMP
            List<tb_tdg_uph_rpt_emp> rptEmpList = perMapper.findRptEmpByConditions(rptid,rptitemid,id,null);
            if (rptEmpList==null || rptEmpList.size() ==0){
                perMapper.insertRPTEmp(functionID,opid,rptitemid,rptid,sec,id,empInfo.getEMP_NAME(),empInfo.getPOS_NAME(),UpDateTime);
            }else{
                perMapper.updateRPTEmpUp(functionID,opid,rptid,id);
            }
            JSONObject j = new JSONObject();
            j.put("rptid",rptid);
            j.put("message","上线成功"+checkSJCLMsg);
            j.put("updatetime",datetime);
            return TDGResponse.success(j);
        } catch (Exception e) {
            _logger.error("员工上线==>异常:{}",e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return TDGResponse.fail(TDGResponseCode.ERROR,e.getCause().getMessage());
        }
    }

    /**
     * 确认下线(单个员工下线)
     * @param sjcl 实际产量
     * @param functionID 模块号
     * @param opid 操作员
     * @param id 员工编号 EMP_ID
     * @param datetime 下线时间
     * @param mo 工单号
     * @param sec 工艺段
     * @param scxm 生产线码
     * @return TDGResponse
     */
    @Transactional
    public TDGResponse confirmDown(String sjcl,String functionID, String opid, String id, String datetime, String mo, String sec, String scxm) {
        _logger.debug("confirmDown ==> Transaction ID: {}", TransactionSynchronizationManager.getCurrentTransactionName());
        _logger.info("员工下线==>模块ID:{}, 实际产量:{},工号:{},工单:{},线码:{},工段:{},下线时间：{},操作员:{}",functionID,sjcl,id,mo,scxm,sec,datetime,opid);

        tb_tdg_uph_emp empInfo = perMapper.findEmpInfoById(id);
        if (empInfo == null){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"该员工不存在或已离职,不可下线");
        }

        List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(id,mo,sec,null);
        if (empList == null || empList.size() == 0) {
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"未找到该员工的当前工单的上线记录");
        }

        VW_TDG_UPH_EMP person = empList.get(0);
        if (person.getSTATE() == 1){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"该员工已下线");
        }
        String rptid = person.getRPT_ID();
        //下线时间
        Date DownDate = null;
        try {
            DownDate = DATETIME_SDF.parse(datetime);
        } catch (ParseException e) {
            _logger.error("员工下线==>日期转换异常:{}",e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return TDGResponse.fail(TDGResponseCode.ERROR,e.getCause().getMessage());
        }

        try {
            //1. 更新考核关系表 TB_TDG_UPH_EMP
            perMapper.updateEmpDown(id,mo,sec);

            /*
            * 上线时间 原则：
            *   最近一次的下线时间 < 在线工单中最早上线时间  则 上线时间 = 在线工单中最早上线时间
             *  最近一次的下线时间 > 在线工单中最早上线时间  则 上线时间 = 最近一次的下线时间
             *  如果没有最近的下线时间   则 上线时间 = 在线工单中最早上线时间
            * */

            //获取最近的一次下线时间
            Date LastDownTime = null;
            List<tb_bs_operate> operateList = perMapper.findOperateByConditions(id,"","","1");
            if (operateList != null && operateList.size() > 0) {
                LastDownTime = DATETIME_SDF.parse(operateList.get(0).getDateTime());
            }
            //当前员工的在线工单列表
            List<VW_TDG_UPH_EMP> onlineEmpList = perMapper.findOnlineEmp(id,null,null,null);
            //列表最早上线时间
            Date MinB_TIME = onlineEmpList.stream()
                            .filter(x -> x.getB_TIME() != null)
                            .min(Comparator.comparing(VW_TDG_UPH_EMP::getB_TIME)).get().getB_TIME();

            Date UpTime = null;
            if (LastDownTime!=null){
                if (LastDownTime.compareTo(MinB_TIME) < 0){
                    UpTime = MinB_TIME;
                } else {
                    UpTime = LastDownTime;
                }
            } else {
                UpTime = MinB_TIME;
            }

            //计算总工作时长
            double TotalWorkTime = 0; //总工作时长  到时候用于计算比例 分摊工时
            //计算总工作时长
            for (int i=0;i<onlineEmpList.size();i++) {
                VW_TDG_UPH_EMP item = onlineEmpList.get(i);
                double workHour = 0;
                if (UpTime.compareTo(item.getB_TIME()) < 0) {
                    //上线时间 < 当前工单的上线时间,以 当前工单的上线时间为准
                    workHour = calcWorkHour(item.getB_TIME(),DownDate);
                } else {
                    //上线时间 > 当前工单的上线时间,以 上线时间为准
                    workHour = calcWorkHour(UpTime,DownDate);
                }
                TotalWorkTime = TotalWorkTime +  workHour;
            }

            //2. 插入上下线操作记录表 tb_bs_operate
            perMapper.insertOperate(functionID,opid,id,person.getEMP_NAME(),1,mo,datetime,rptid,sec);

            //3. 插入员工上下线记录表 tb_qywx_report (workTime此时不考虑均摊)
            List<tb_bs_operate> operates = perMapper.findOperateByConditions(id,mo,rptid,"0");
            if(operates != null && operates.size() > 0){
                // 间隔 = 下线时间 - 上线时间
                Date opUpTime = DATETIME_SDF.parse(operates.get(0).getDateTime());
                double workHour = calcWorkHour(opUpTime,DownDate);
                perMapper.insertReport(functionID,opid,id,person.getEMP_NAME(),person.getMO(),workHour,datetime,person.getRPT_ID(),sec);
            }

            //4. 更新员工报工表 TB_TDG_UPH_RPT_EMP  MO_WT需要考虑按比例计算
            /* MO_WT = (下线时间 - 上线时间) *  本工单工作时长 / 总工作时长 */
            double CalWorkHour = calcWorkHour(UpTime,DownDate);

            for (int i = 0; i < onlineEmpList.size(); i++) {
                VW_TDG_UPH_EMP item = onlineEmpList.get(i);
                double curWorkTime = 0;
                if (UpTime.compareTo(item.getB_TIME()) < 0 ){
                    curWorkTime=calcWorkHour(item.getB_TIME(),DownDate);
                } else {
                    curWorkTime=calcWorkHour(UpTime,DownDate);
                }
                //原有的WO_MT + 此次分配的WO_MT
                Double MO_WT = item.getMO_WT();
                if (TotalWorkTime != 0){
                    MO_WT = MO_WT + new BigDecimal(curWorkTime * (CalWorkHour/TotalWorkTime)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                }

                if (mo.equalsIgnoreCase(item.getMO()) && sec.equalsIgnoreCase(item.getSEC())) {
                    perMapper.updateRPTEmpDown(functionID,opid,DownDate,MO_WT, item.getEMP_ID(), item.getRPT_ID(),item.getRPT_ITEM_ID());
                } else {
                    perMapper.updateRPTEmpWOMT(opid,MO_WT,item.getEMP_ID(),item.getRPT_ID(),item.getRPT_ITEM_ID());
                }
            }

            //5.本工单工序 全部人员下线后 计算工时并分摊  rpt变为关闭，且更新rptemp的生产结束时间和工单考勤工时
            List<VW_TDG_UPH_EMP> personList = perMapper.findOnlineEmp(null,mo, sec,null);
            if(personList == null || personList.size() == 0){
                TDGResponse tdgResponse = setRPTDown(sjcl,functionID,opid,rptid,DownDate,mo,sec);
                if (tdgResponse.getCode() != 200){
                    return tdgResponse;
                }
            }

            JSONObject j = new JSONObject();
            j.put("message","下线成功");
            j.put("downdatetime",datetime);

            return TDGResponse.success(j);
        } catch (Exception e) {
            _logger.error("员工下线==>异常:{}",e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"下线异常:"+e.getCause().getMessage());
        }
    }

    /**
     * 勾选下线
     * @param sjcl 实际产量
     * @param functionID 模块号
     * @param opid 操作员
     * @param gdh 工单号
     * @param ryarr 下线员工数组
     * @param datetime 下线时间
     * @param scxm 生产线码
     * @param sec 工艺段
     * @return TDGResponse
     */
    @Transactional
    public TDGResponse downByCheck(String sjcl,String functionID,String opid, String gdh,String[] ryarr,String datetime,String scxm,String sec) {
        _logger.debug("downByCheck ==> Transaction ID: {}", TransactionSynchronizationManager.getCurrentTransactionName());
        _logger.info("员工勾选下线==>模块ID:{}, 实际产量:{}, 勾选工号:{},工单:{},工段:{},生产线码:{},下线时间：{},操作员:{}",functionID,sjcl,ryarr,gdh,sec,scxm,datetime,opid);

        for (int i=0;i<ryarr.length;i++) {
            String id = ryarr[i];
            List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(id,gdh,sec,null);
            if (empList != null && empList.size() > 0){
                VW_TDG_UPH_EMP person = empList.get(0);
                TDGResponse response = confirmDown(sjcl,functionID,opid,id, datetime, gdh, sec, scxm);
                if (response.getCode() != 200){
                    _logger.error("员工勾选下线==>异常:人员:{},姓名:{},下线结果:{}",person.getEMP_ID(),person.getEMP_NAME(),response.getMessage());
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return response;
                }
            } else {
                TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"未找到该工卡工单的上线记录");
            }
        }
        return TDGResponse.success("勾选下线成功");
    }

    /**
     * 整工单下线(遍历员工下线)
     * @param sjcl 实际产量
     * @param functionID 模块号
     * @param opid 操作员
     * @param gdh 工单号
     * @param datetime 下线时间
     * @param sec 工艺段
     * @param scxm 生产线码
     * @param bb 班别 0:白班  1:晚班
     * @return TDGResponse
     */
    @Transactional
    public TDGResponse downByGD(String sjcl,String functionID,String opid, String gdh,String datetime,String sec,String scxm,String bb) {
        _logger.debug("downByGD ==> Transaction ID: {}", TransactionSynchronizationManager.getCurrentTransactionName());
        _logger.info("整工单下线==>模块ID:{}, 实际产量:{}, 工单:{},线码:{},工段:{},班别{} ,下线时间：{},操作员:{}",functionID,sjcl,gdh,scxm,sec,bb,datetime,opid);

        List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(null,gdh, sec,null);
        if (empList ==null || empList.size()==0){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"当前工单不存在上线人员");
        }

        for (int i=0;i<empList.size();i++){
            VW_TDG_UPH_EMP person = empList.get(i);
            if(person!=null){
                TDGResponse response = confirmDown(sjcl,functionID,opid,person.getEMP_ID(), datetime, gdh, sec, scxm);
                if (response.getCode() != 200){
                    _logger.error("整工单下线==>异常:人员:{},姓名：{},下线结果:{}",person.getEMP_ID(),person.getEMP_NAME(),response.getMessage());
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return response;
                }
            }
        }
        return TDGResponse.success("整工单下线成功");
    }

    /**
     * 勾选切换工单
     * @param functionID 模块号
     * @param opid 操作员
     * @param gdh 原始工单号
     * @param gdh2 目标工单号
     * @param scxm 原始生产线码
     * @param scxm2 目标生产线码
     * @param datetime 切换时间
     * @param sec 原始工艺段
     * @param sec2 目标工艺段
     * @param bb 班别
     * @param ryarr 勾选人员
     * @return
     */
    @Transactional
    public TDGResponse qhgd(String sjcl,String functionID,String opid, String gdh, String gdh2, String scxm,
                            String scxm2, String datetime, String sec, String sec2, String bb,String[] ryarr) {
        _logger.debug("qhgd ==> Transaction ID: {}", TransactionSynchronizationManager.getCurrentTransactionName());
        _logger.info("切换工单==>模块ID:{},实际产量:{},原工单:{},原线码:{},原工段:{},目标工单:{},目标线码:{},目标工段:{} ,班别:{},切换时间：{},切换人员:{},操作员:{}",
                    functionID,sjcl,gdh,scxm,sec,gdh2,scxm2,sec2,bb,datetime,ryarr,opid);

        if(ryarr == null || ryarr.length == 0){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"未勾选切换人员,不能切换工单!");
        }

        List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(null,gdh,sec,null);
        if(empList.size()==0){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"当前工单不存在上线人员");
        }

        List<VW_TDG_UPH_EMP> descList = perMapper.findOnlineEmp(null,gdh2,sec2,null);
        if (descList != null && descList.size() > 0){
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"目标工单已有人员上线,不允许切换上线");
        }

        //目标工单不允许有上线人员
        for (int i=0;i<ryarr.length;i++) {
            String id = ryarr[i];
            List<VW_TDG_UPH_EMP> onlineEmpList = perMapper.findOnlineEmp(id,gdh,sec,null);
            if (onlineEmpList != null && onlineEmpList.size() > 0){
                VW_TDG_UPH_EMP emp = onlineEmpList.get(0);
                TDGResponse response = confirmDown(sjcl,functionID,opid,id, datetime, gdh, sec, scxm);
                if (response.getCode() != 200){
                    _logger.error("切换工单==>下线异常:人员:{},姓名:{},下线结果:{}",emp.getEMP_ID(),emp.getEMP_NAME(),response.getMessage());
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return response;
                }
            } else {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"未找到该工卡工单的上线记录");
            }
        }

        for (int i=0;i<ryarr.length;i++) {
            String id = ryarr[i];
            tb_tdg_uph_emp empInfo = perMapper.findEmpInfoById(id);
            TDGResponse response = confirmUp(functionID+ "_UP",opid,empInfo.getEMP_ID(),empInfo.getEMP_NAME(),bb,gdh2,scxm2,datetime,sec2);
            if (response.getCode() != 200){
                _logger.error("切换工单==>上线异常:人员:{},姓名:{},上线结果:{}",empInfo.getEMP_ID(),empInfo.getEMP_NAME(),response.getMessage());
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return response;
            }

        }

        return TDGResponse.success("切换工单成功");
    }

    /**
     * 最后一个人下线或 全部下线 需判断是否需要弹框输入实际产量
     * @param mo 工单号
     * @param sec 工艺段
     * @param scxm 生产线码
     * @return SMT不需要弹框  非SMT只有没有产能工序才需要弹框
     */
    public TDGResponse pdsjcl(String[] ryarr,String mo, String sec, String scxm){
        JSONObject j = new JSONObject();
        List<tb_tdg_uph_rpt> rptList = perMapper.findRptByConditions(mo,scxm,"0",sec);
        if (rptList != null && rptList.size()>0) {
            j.put("gdh",rptList.get(0).getMO());
            j.put("scxm",rptList.get(0).getLINE_ID());
            j.put("bb",rptList.get(0).getSHIFT_TYPE());

        }
        //判断是否最后一人 或者 全部下线
        List<VW_TDG_UPH_EMP> empList = perMapper.findOnlineEmp(null,mo,sec,null);
        if (empList == null || empList.size()==0){
            j.put("isShow","N");
            j.put("message","该工单无上线人员,请刷新数据");
            return TDGResponse.success(j);
        }

        ArrayList<String> DownEmolist = new ArrayList<>(Arrays.asList(ryarr));

        for (int i= empList.size() -1  ;i >= 0;i--){
            if (DownEmolist.contains(empList.get(i).getEMP_ID())) {
                empList.remove(empList.get(i));
            }
        }

        if (empList.size()==0){
            tb_pp_mo moInfo = moMapper.findMoById(mo);
            //TP_PHASE=1 表示SMT  Y一下判断如果是非SMT的 则需弹框，是
            String TP_PHASE = moInfo.getTP_PHASE().toString();

            if("1".equals(TP_PHASE)){
                j.put("isShow","N");
                j.put("message","SMT自动计算产量,不需要弹框输入");
            } else {
                List<Map<String, Object>> cngxMap = perMapper.findCNGX(publicUtil.getGdh1(mo),sec);
                if(cngxMap.size()==0){
                    j.put("isShow","Y");
                    j.put("message","无产能工序，需弹框输入实际产量");
                } else {
                    j.put("isShow","N");
                    j.put("message","有产能工序，不需要弹框输入");
                }
            }
            return TDGResponse.success(j);
        } else {
            j.put("isShow","N"); //还有人未下线 则不弹框
            j.put("message","还有人未下线,不需要弹框输入");
        }
        return TDGResponse.success(j);
    }

    /**
     * 更新实际产量
     * @param opid 操作员
     * @param sjcl1 实际产量
     * @param sjcl1 实际产量
     * @param gdh 工单号
     * @param scxm 生产线码
     * @param bb 班别
     * @param sec 工艺段
     * @return
     */
    @Transactional
    public TDGResponse updatesjcl(String opid, String sjcl1,String gdh,String scxm,String bb,String sec){
        _logger.debug("updatesjcl ==> Transaction ID: {}", TransactionSynchronizationManager.getCurrentTransactionName());
        _logger.info("根据实际产量计算报工数据==>工单:{},线码:{},班别:{},工艺段:{},实际产量：{},操作员:{}",gdh,scxm,bb,sec,sjcl1,opid);

        List<tb_tdg_uph_rpt> rptList = perMapper.findRptByConditions(gdh,scxm,"1",sec);
        if(rptList!= null && rptList.size()>0) {
            String rptid = rptList.get(0).getRPT_ID();
            List<tb_tdg_uph_rpt_item> itemList = perMapper.findRptItemByConditions(rptid,sec,"1",gdh);

            if (itemList == null && itemList.size() == 0) {
                return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"不存在对应的报工明细数据，请核查");
            }

            tb_tdg_uph_rpt_item rptItem = itemList.get(0);
            Double zsl = rptItem.getP_QTY();//总数量

            Double yscsl = getYSCSL(gdh,rptItem.getPROD_SEC()); //已生产数量
            Double sysl = DoubleUtil.sub(zsl,yscsl); //剩余数量
            Double sjcl =  Double.valueOf("0.00"); //实际产量
            if(new BigDecimal(sjcl1).compareTo(new BigDecimal(sysl))>0){
//                return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"实际产量不能大于剩余数量，请核查");
                sjcl = Double.valueOf("0.00");
            } else {
                sjcl = Double.valueOf(sjcl1); //实际产量
            }

            Double bzde = Double.valueOf("0.00");//标准定额
            Double rs = Double.valueOf("0.00");//人数
            Double zjsj = Double.valueOf("0.00");//转机时间
            Double tpts = Double.valueOf("0.00");//贴片机台数

            List<Map<String, Object>> datamap = mtrlMapper.selectBZDE(publicUtil.getGdh1(gdh),sec,scxm);
            if(datamap.size()>0){
                if(null!=datamap.get(0).get("UPH")){
                    bzde = Double.parseDouble(datamap.get(0).get("UPH").toString());
                }
                if(null!=datamap.get(0).get("UPH_DL")){
                    rs = Double.parseDouble(datamap.get(0).get("UPH_DL").toString());
                }

                //只有第一个工单报工才需要记录转机时间 后续的则给 不给转机时间
                if (rptList != null && rptList.size() == 1 ) {
                    if(null!=datamap.get(0).get("CO_WT")){
                        zjsj = Double.parseDouble(datamap.get(0).get("CO_WT").toString());
                        zjsj = DoubleUtil.div(zjsj,Double.valueOf("3600.00"));
                    }
                }else{
                    zjsj = Double.valueOf("0.00");
                }

                if(null!=datamap.get(0).get("HSP_DEV_QT")){
                    tpts = Double.parseDouble(datamap.get(0).get("HSP_DEV_QT").toString());
                }
            }

            Double xygrs = Double.valueOf("0.00");//新员工人数
            Double xygbl = Double.valueOf("0.00");//新员工比例

            List<tb_tdg_uph_rpt_emp> sjrslist = perMapper.findRptEmpByConditions(rptid,"","","1");
            Double sjrs = Double.parseDouble(String.valueOf(sjrslist.size()));
            if(sjrslist.size()>0){
                for (tb_tdg_uph_rpt_emp iter : sjrslist) {
                    if("新员工".equals(iter.getEMP_POS())){
                        xygrs = DoubleUtil.add(xygrs,1.00);
                    }
                }
            }
            //新员工比例 = 新员工人数 / 实际总人数
            xygbl = DoubleUtil.div(xygrs,sjrs);

            //计算定额 =（标准定额*实际人数）/标准定额人数
            Double jsde = Double.valueOf("0.00");
            if(rs!=0.00){
                jsde =DoubleUtil.div(DoubleUtil.mul(bzde,sjrs),rs);
            }

            //产出工时 = 实际产量 /标准定额
            Double ccgs = Double.valueOf("0.00");
            if(bzde!=0.00){
                ccgs = DoubleUtil.div(sjcl,bzde);
            }

            //获得工时 = （标准定额_转机时间 + 产出工时）* 标准定额人数
            Double hdgs = DoubleUtil.mul(DoubleUtil.add(zjsj, ccgs),rs);
            Double hdgsdis = DoubleUtil.divDis(hdgs,sjrs);

            //线体工时
            double xtgs = calcWorkHour(rptItem.getB_TIME(),rptItem.getE_TIME());

            //工单总考勤时间
            Map<String, Object> mapSUMWT = perMapper.findSumWorkTime(rptid);
            Double sumWT = Double.valueOf(mapSUMWT.get("SUMWORKTIME").toString());

            //工时利用率 = 获得工时/ 工单总考勤时间；
            double gslyl = Double.valueOf("0.00");
            if(new BigDecimal(sumWT).compareTo(BigDecimal.ZERO)>0){
                gslyl = DoubleUtil.div(hdgs,sumWT);
            }
            //5.更新报工表 TB_TDG_UPH_RPT
            perMapper.updateRPT(opid,sjrs,gslyl,rptid);
            //6.更新报工项目表 TB_TDG_UPH_RPT_Item
            perMapper.updateRptItem(opid,bzde,rs,sjrs,jsde,xygbl,sjcl,ccgs,hdgs,xtgs,gslyl,zjsj,tpts,sumWT,rptItem.getRPT_ITEM_ID());
            //修改单个人的获得工时，根据工单考勤时间进行比例分配；
            setHDWT(opid,rptid,hdgs);
        }
        return TDGResponse.success("更新实际产量OK");
    }

    /**
     * 报工表 和 报工项目表的关闭
     * @param functionID 模块号
     * @param opid 操作员
     * @param rptid
     * @param downdate
     */
    @Transactional
    public TDGResponse setRPTDown(String sjclInput,String functionID,String opid, String rptid,Date downdate,String mo,String sec){
        _logger.debug("setRPTDown ==> Transaction ID: {}", TransactionSynchronizationManager.getCurrentTransactionName());
        _logger.info("工单下线关闭==>模块ID:{},输入的实际产量:{},工单:{},RPT_ID:{},原工段:{},下线时间：{},操作员:{}",
                functionID,sjclInput,mo,rptid,sec,downdate,opid);

        tb_tdg_uph_rpt rpt = perMapper.findRptById(rptid);
        if(rpt!=null){
            String remark = "_";
            if(null!=rpt.getREMARK()){
                remark = rpt.getREMARK();
            }else{
                remark = rpt.getRPT_STATE();
            }
            perMapper.updateRPTDown(functionID,opid,remark,rptid);
            perMapper.updateRptItemDown(functionID,opid,downdate,rptid);
            //获取实际产量
            double sjcl = getSJCL(functionID,opid,rptid,downdate,mo,sec);
            if (new BigDecimal(sjcl).compareTo(new BigDecimal(Double.valueOf(sjclInput))) < 0){
                sjcl = Double.valueOf(sjclInput);
            }
            TDGResponse response = updatesjcl(opid,Double.toString(sjcl),mo,rpt.getLINE_ID(),rpt.getSHIFT_TYPE(),sec);
            if (response.getCode() != 200 ){
                _logger.error("工单下线关闭==>异常:工单:{},工艺段:{},下线异常:{}",mo,sec,response.getMessage());
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return response;
            }
        } else {
            _logger.error("工单下线关闭==>未找到对应的可报工关闭记录 : 模块ID:{},输入的实际产量:{},工单:{},RPT_ID:{},原工段:{},下线时间：{},操作员:{}",
                    functionID,sjclInput,mo,rptid,sec,downdate,opid);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return TDGResponse.fail(TDGResponseCode.BUSINESS_ERRPR,"工单下线关闭异常：未找到对应的可报工关闭记录");
        }
        return TDGResponse.success("报工关闭OK");
    }

    /**
     * 下线时获取实际产量
     * @param rptid
     * @param downdate
     * @return
     */
    public double getSJCL(String functionID,String opid,String rptid,Date downdate,String gdh,String sec){
        Double sjcl = Double.valueOf("0.00"); //实际产量
        tb_pp_mo moInfo = moMapper.findMoById(gdh);

        List<tb_tdg_uph_rpt_item> rptItemList = perMapper.findRptItemByConditions(rptid,sec,"1",gdh);
        tb_tdg_uph_rpt_item rptItemInfo = null;
        if (rptItemList != null && rptItemList.size() > 0) {
            rptItemInfo = rptItemList.get(0);
        } else {
            _logger.error("数据异常：未查询到报工项目表(TB_TDG_UPH_RPT_ITEM)的在线数据");
        }

        Date btime = rptItemInfo.getB_TIME();

        Double tbqty = moInfo.getTB_QTY(); //总产量
        Double jhsl = moInfo.getP_QTY(); //计划数量
        //如果总产量>计划数量  则总产量=计划数量
        if(BigDecimal.valueOf(tbqty).compareTo(BigDecimal.valueOf(jhsl))>0){
            tbqty = jhsl;
        }
        if(moInfo.getTP_PHASE() == 1){
            //根据工单号去TB_TDG_UPH_SMT_SJCL查找上一次产量  selectSMTLastCL 如果有一次异常  则后续都计算错误
//            List<Map<String, Object>>  smtlastclmap  = mtrlMapper.selectSMTLastCL(gdh);
//            if(smtlastclmap.size()>0){
//                Double lastcl = Double.valueOf(smtlastclmap.get(0).get("SJCL").toString());
//                //当SMT工单上下线的产量计算  TB_QTY -上一次的
//                sjcl = DoubleUtil.sub(tbqty,lastcl);
//            }else{
//                sjcl = tbqty;
//            }

            Double yscsl = getYSCSL(gdh,sec);//已生产数量
            //实际产量 = 总过板数 - 已生产数量
            sjcl = DoubleUtil.sub(tbqty,yscsl);
            mtrlMapper.insertSMTSJCL(functionID,opid,gdh,tbqty);
        }else{
            List<Map<String, Object>> sjclmap = mtrlMapper.selectSJCL(publicUtil.getGdh1(gdh),gdh,sec,btime,downdate);
            if(sjclmap!= null && sjclmap.size()>0){
                if(null!=sjclmap.get(0) && null!=sjclmap.get(0).get("SJCL")){
                    sjcl = Double.valueOf(sjclmap.get(0).get("SJCL").toString());
                }
            }
        }
        _logger.info("获取实际产量==> 模块ID：{},工单:{},工艺段：{},总产量(总过板数)：{},计划数量:{},操作员:{},实际产量:{}",functionID,gdh,moInfo.getTP_PHASE(),tbqty,jhsl,opid,sjcl);
        return sjcl;
    }

    /**
     * 遍历更新 整个工单的 TB_TDG_UPH_RPT_EMP.RPT_WT
     * @param opid 操作员
     * @param rptid 报工ID
     * @param hdgs 获得工时
     */
    public void setHDWT(String opid,String rptid,Double hdgs) {
        List<tb_tdg_uph_rpt_emp> rptEmpList = perMapper.findRptEmpByConditions(rptid,null,null,"1");
        Map<String, Object> mapSUMWT = perMapper.findSumWorkTime(rptid);
        Double sumWT = Double.valueOf(mapSUMWT.get("SUMWORKTIME").toString());
        if(new BigDecimal(sumWT).compareTo(BigDecimal.ZERO)>0){
            for (tb_tdg_uph_rpt_emp emp : rptEmpList) {
                //单个员工的工单考勤时间/所有人总工单考勤时间*总获得工时
                Double hdgsdisByID = DoubleUtil.mulDis(DoubleUtil.divDisById(emp.getMO_WT(),sumWT),hdgs);
                _logger.info("更新个人工时:工号:{},工单考勤时间:{},获得工时:{}",emp.getEMP_ID(),emp.getMO_WT(),hdgsdisByID);
                perMapper.updateRptEmpRPTWT(opid,hdgsdisByID,rptid,emp.getEMP_ID());
            }
        }
    }

}

